using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Reflection;
using MacomberMap.Common.Database_Interface;
using MacomberMap.Common.Internals;
using MacomberMap.Common.User_Interfaces.Network_Map;
using MacomberMap.Common.Logging;
using MacomberMap.Common.Events;
using MacomberMap.Loader.User_Interfaces.Network_Map;
using MacomberMap.Loader.User_Interfaces.UserRoles;
using MacomberMap.Common.User_Interfaces.Database;
using MacomberMap.Common.Permissions;
using MacomberMap.Loader.User_Interfaces.CIM;
using MacomberMap.Common.Database_Interface.CIM;
using MacomberMap.Common.Database_Interface.CIM.User_Interfaces;
using System.IO;

namespace MacomberMap.Loader.User_Interfaces.Database
{
    /// <summary>
    /// This class presents the first step in using the MM Loader - connecting to a back-end database
    /// </summary>
    public partial class frmDatabase : Form
    {
        #region Variable declarations
        /// <summary>Our existing database connection</summary>
        public MM_Database_Oracle dbConnection = null;

        /// <summary>The CIM database</summary>
        public MM_Database_CIM CIMDb=null;
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize the database form
        /// </summary>
        public frmDatabase()
        {
            InitializeComponent();

            //Update our version 
            this.lblVersion.Text = "Version " + Application.ProductVersion + " (Core " + MM_Repository.BaseVersion + ")";


            //Update our databases
            foreach (MM_Database Db in MM_Configuration.Databases.Values)
                this.cmbExistingConnections.Items.Add(Db.Name);
            this.cmbExistingConnections.Items.Add("CIM Browser");
            this.cmbExistingConnections.Items.Add("Savecase");
            this.cmbExistingConnections.Items.Add("Advanced...");
            this.Size = new Size(429, 126);
           

        }
        #endregion

        #region Predefined database selection
        /// <summary>
        /// Handle the user's selecting an existing database connection or advanced mode
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbExistingConnections_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmbExistingConnections.Text == "Advanced...")
                this.Size = new Size(757, 523);
            else if (cmbExistingConnections.Text == "Savecase")
                using (OpenFileDialog oFd = new OpenFileDialog())
                {
                    oFd.Title = "Open a Macomber Map savecase";
                    oFd.Filter = "Macomber Map Savecase (*.MM_Model)|*.MM_Model";
                    if (oFd.ShowDialog() != DialogResult.OK)
                        return;
                    dbConnection = new MM_Database_Savecase(oFd.FileName);
                    btnManageOrConnectOrKeyOnly_Click(frmCIM.PerformSynch ? btnConnect : btnKeyOnly, e);                        
                    
                }
            else if (cmbExistingConnections.Text == "CIM Browser")
            {
                //Invite the user to choose a CIM file.
                OpenFileDialog o = new OpenFileDialog();
                o.Title = "Open a CIM database";
                o.Filter = "CIM Databases and Indexes (*.xml;*.CimIndex;*.idx)|*.xml;*.CimIndex;*.idx|CIM Database (*.xml)|*.xml|CIM Index (*.CimIndex;*.idx)|*.CimIndex;*.idx|All files (*.*)|*.*";
                if (o.ShowDialog() == DialogResult.OK)
                {                    
                    CIMDb = new MM_Database_CIM(o.FileName);
                    evLog.AssignConnectors(CIMDb);
                    tmrUpdate.Enabled = true;
                    while (!MM_Database_CIM.CIMLoader.Completed)
                        Application.DoEvents();
                    new frmCIMBrowser().Show();
                }
            }
            else
            {
                dbConnection = MM_Configuration.Databases[cmbExistingConnections.Text] as MM_Database_Oracle;
                cmbDbType.Text = dbConnection.GetType().Name.Split('_')[2];
                FieldInfo[] inFields = dbConnection.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                foreach (FieldInfo fI in inFields)
                    if (fI.Name == "Database")
                        cmbDbName.Text = fI.GetValue(dbConnection).ToString();
                    else if (fI.Name == "Username")
                        txtDbUsername.Text = fI.GetValue(dbConnection).ToString();
                    else if (fI.Name == "Password")
                        txtPassword.Text = fI.GetValue(dbConnection).ToString();

                if (this.Height != 523)
                    btnManageOrConnectOrKeyOnly_Click(frmCIM.PerformSynch ? btnConnect : btnKeyOnly, e);                        
            }
        }
        #endregion

        #region Database Type selection
        /// <summary>
        /// Update our information based on the selected database type
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbDbType_SelectedIndexChanged(object sender, EventArgs e)
        {
            MM_Database.ConfigureExistingSchemaUI(cmbDbType.Text, cmbDbName, txtDbUsername, txtPassword, cmbSchema);
        }
        #endregion

        #region Button handling        
        /// <summary>
        /// Connect to a database, and manage it.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnManageOrConnectOrKeyOnly_Click(object sender, EventArgs e)
        {
            if (dbConnection != null && dbConnection.State != ConnectionState.Open)
                //try
                {
                    evLog.AssignConnectors(dbConnection);

                    DateTime StartTime = DateTime.Now;
                    dbConnection.Open();
                    MM_Log.LogEvent(new MM_Event_Information("Connected to " + dbConnection.Name + ": " + (DateTime.Now-StartTime), this));

                    StartTime = DateTime.Now;
                    MM_Coordinate_Interface.LoadCoreData(dbConnection, sender != btnKeyOnly);
                    MM_Log.LogEvent(new MM_Event_Information("Loaded core data: " + (DateTime.Now - StartTime), this));

                    StartTime = DateTime.Now;
                    MM_User.LoginCurrentUser(dbConnection);
                    MM_Log.LogEvent(new MM_Event_Information("User logged in: " + (DateTime.Now-StartTime), this));
                  
                    if (sender == btnManage)                    
                    {
                        StartTime = DateTime.Now;
                        dgvDbStats.DataSource = dbConnection.ConnectionStats();
                        MM_Log.LogEvent(new MM_Event_Information("Loaded connection stats: " + (DateTime.Now - StartTime), this));


                        if (tvDatabases.TabPages.Contains(tpCreate))
                            tvDatabases.TabPages.Remove(tpCreate);
                        if (!tvDatabases.TabPages.Contains(tpManagement))
                            tvDatabases.Controls.Add(tpManagement);
                    }                                        
                }
                //catch (Exception ex)
                //{
                //    MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error opening database in management mode", ex)));
                //    return;
               // }

            if (dbConnection != null && (sender == btnConnect || sender == btnKeyOnly)&& dbConnection.State == ConnectionState.Open)
            {
                        GC.Collect();
                        this.DialogResult = DialogResult.OK;
                        this.Close();
                        return;
            }
        }

        /// <summary>
        /// Handle the user requesting the editing of station names/coordinates
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnEditStationNames_Click(object sender, EventArgs e)
        {
            new frmNetworkMapEditor().Show();
        }

        /// <summary>
        /// Import the station names from either our Excel spreadsheet or our 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImportStationNames_Click(object sender, EventArgs e)
        {
            OpenFileDialog o = new OpenFileDialog();
            o.Title = "Import a substation lat/long file";
            o.Filter = "Comma-delimited list (*.csv)|*.csv|Excel spreadsheet (*.xls*)|*.xls*";
            if (o.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    MM_Coordinate_Interface.LoadCoordinates(o.FileName);
                }
                catch (Exception ex)
                {
                    MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error in loading and saving coordinates", ex)));
                }
            }
        }



        /// <summary>
        /// Either (1) Upload an existing state boundary, or (2) Build a state boundary from the borders of the counties
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUploadStateBoundary_Click(object sender, EventArgs e)
        {
            OpenFileDialog o = new OpenFileDialog();
            o.Title = "Open a state boundary file";
            o.Filter = "State boundary (*.csv)|*.csv";
            if (o.ShowDialog() == DialogResult.OK)
                try
                {
                    MM_Coordinate_Interface.UploadStateBoundary(o.FileName, dbConnection);
                }
                catch (Exception ex)
                {
                    MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error in uploading the state boundary", ex)));
                }
        }

        /// <summary>
        /// Recompute the state boundary
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRecomputeStateBoundary_Click(object sender, EventArgs e)
        {
            MM_Coordinate_Interface.RebuildStateBoundary(dbConnection);
        }

        /// <summary>
        /// Blank the data within the database
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReset_Click(object sender, EventArgs e)
        {
            if (dbConnection != null)
                if (dbConnection != null)
                    try
                    {
                        dbConnection.BlankDatabase();
                    }
                    catch (Exception ex)
                    {
                        MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error in blanking database", ex)));
                    }
        }


        /// <summary>
        /// Create or re-create our tables within the current schema
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCreateTables_Click(object sender, EventArgs e)
        {
            if (dbConnection != null)
                try
                {
                    dbConnection.CreateTables(null);
                }
                catch (Exception ex)
                {
                    MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error in creating tables", ex)));
                }
        }



        /// <summary>
        /// Create a new database schema
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmdCreateSchema_NewDb_Click(object sender, EventArgs e)
        {
            if (dbConnection != null)
                try
                {
                    MM_Database_Oracle NewDatabase = MM_Database.CreateSchema(cmbDatabaseType_CreateSchema.Text, txtConnectionName.Text, txtAdminUsername.Text, txtAdminPassword.Text, cmbDatabase_CreateSchema.Text, txtUserName_NewDb.Text, txtPassword_NewDb.Text, txtDataPath_NewDb.Text, txtSchemaSize_NewDb.Value, txtMaxSize_NewDb.Value, txtExtStep_NewDb.Value) as MM_Database_Oracle;
                    if (MessageBox.Show("Would you like to add the new schema to the list of established connections?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        MM_Configuration.Databases.Add(NewDatabase.Name, NewDatabase);
                        MM_Configuration.SaveConfiguration();
                    }
                    if (MessageBox.Show("Would you like to connect to the new database?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        dbConnection = NewDatabase;
                        btnManageOrConnectOrKeyOnly_Click(sender, e);
                    }
                }
                catch (Exception ex)
                {
                    MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error in creating tables", ex)));
                }
        }

        /// <summary>
        /// Upload a county's website list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUploadCountyWebsites_Click(object sender, EventArgs e)
        {
            if (dbConnection != null)
                try
                {
                    OpenFileDialog o = new OpenFileDialog();
                    o.Title = "Open county website list";
                    o.Filter = "County website information (*.csv)|*.csv";
                    if (o.ShowDialog() != DialogResult.OK)
                        return;
                    dbConnection.SaveCountyWebsites(o.FileName);
                }
                catch (Exception ex)
                {
                    MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error in uploading county websites", ex)));
                }
        }

        /// <summary>
        /// Upload a county's coordinates from a shapefile
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUploadCountyBoundary_Click(object sender, EventArgs e)
        {
            if (dbConnection != null)
                try
                {
                    OpenFileDialog o = new OpenFileDialog();
                    o.Title = "Locate GIS counties to upload";
                    o.Filter = "GIS County information (*.shp)|*.shp";
                    o.Multiselect = true;
                    if (o.ShowDialog() != DialogResult.OK)
                        return;
                    foreach (String FileName in o.FileNames)
                        MM_Coordinate_Interface.ConvertCountyCoordinates(FileName, dbConnection);
                }
                catch (Exception ex)
                {
                    MM_Log.LogEvent(new MM_Event_SystemError(new Exception("Error in uploading county coordinates", ex)));
                }
        }

        #endregion

        /// <summary>
        /// Update a label's visibility when the calling object's visibility changes
        /// </summary>
        /// <param name="sender">The object being modified</param>
        /// <param name="e"></param>
        private void UpdateVisibility(object sender, EventArgs e)
        {
            Control Sender = (Control)sender;
            tpExisting.Controls["lbl" + Sender.Name.Substring(3)].Visible = Sender.Visible;
        }

        /// <summary>
        /// Display the editor for roles, users, and databases
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditRolesUsersAndDatabases(object sender, EventArgs e)
        {
            if (sender == btnRoles)
                new frmUserRoles(dbConnection, 0).ShowDialog();
            else if (sender == btnUsers)
                new frmUserRoles(dbConnection, 1).ShowDialog();
        }

        /// <summary>
        /// Backup the schema to an XML file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnBackupSchema_Click(object sender, EventArgs e)
        {
            string[] inResp;
            using (frmTableSelector tbl = new frmTableSelector(dbConnection.ConnectionStats()))
                if (tbl.ShowDialog() == DialogResult.OK)
                    inResp = tbl.SelectedTables;
                else
                    return;

            using (SaveFileDialog s = new SaveFileDialog())
            {
                s.Title = "Save XML Schema backup...";
                s.Filter = "Compressed XML backup (*.xml.gz)|*.xml.gz|XML Backup (*.xml)|*.xml";
                if (s.ShowDialog() == DialogResult.OK)
                {
                    dbConnection.BackupSchema(s.FileName, inResp);
                    MessageBox.Show("Schema backup completed", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }

        /// <summary>
        /// Restore a schema into the database
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRestoreSchema_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog o = new OpenFileDialog())
            {
                o.Title = "Load XML Schema backup...";
                o.Filter = "Compressed XML backup (*.xml.gz)|*.xml.gz|XML Backup (*.xml)|*.xml|Macomber Map Model (*.MM_Model)|*.MM_Model";
                if (o.ShowDialog() == DialogResult.OK)
                    MessageBox.Show("Schema restore completed. " + dbConnection.RestoreSchema(o.FileName).ToString("#,##0") + " rows affected.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        #region Event handler
        //When our form pops up, assign the event handlers.
        private void frmDatabase_Shown(object sender, EventArgs e)
        {
            evLog.AssignEventHandlers(TestIncludeEvent);
        }

        //When our form is closing, remove the event handlers
        private void frmDatabase_FormClosing(object sender, FormClosingEventArgs e)
        {
            evLog.RemoveEventHandlers();
        }

        /// <summary>
        /// Determine whether an event should be added, or auto-processed.
        /// </summary>
        /// <param name="Event"></param>
        /// <returns></returns>
        private bool TestIncludeEvent(MM_Event Event)
        {
            return true;
        }
        #endregion

        

        /// <summary>
        /// Update the CIM file loading updater
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        //private void picUpdate_Paint(object sender, PaintEventArgs e)
        //{
        //    if (MM_Database_CIM.CIMLoader == null)
        //        return;
        //    else
        //    {
        //        e.Graphics.Clear(SystemColors.Control);

        //        if (!MM_Database_CIM.CIMLoader.Completed)
        //        {
        //            //Determine our percentage and estmated time remaining
        //            long Position =MM_Database_CIM.CIMLoader.Position;
        //            long FileSize = MM_Database_CIM.CIMLoader.FileSize;
        //            if (FileSize == 0)
        //                return;

        //            string Title = "Loading";
        //            float Percentage = (float)Position / (float)FileSize;

        //            int CurWidth = (int)(picUpdate.Width * Percentage);
        //            e.Graphics.FillRectangle(Brushes.LightBlue, new Rectangle(0, 0, CurWidth, picUpdate.Height));
        //            e.Graphics.DrawRectangle(Pens.Black, new Rectangle(0, 0, picUpdate.Width - 1, picUpdate.Height - 1));
        //            StringFormat sF = new StringFormat();
        //            sF.Alignment = StringAlignment.Center;
        //            sF.LineAlignment = StringAlignment.Center;
        //            if (Position > 0)
        //            {
        //                TimeSpan RunTime = DateTime.Now - MM_Database_CIM.CIMLoader.StartTime;
        //                long Remaining = FileSize - Position;
        //                TimeSpan RemainingTime = TimeSpan.FromSeconds(Remaining * (RunTime.TotalSeconds / Position));
        //                e.Graphics.DrawString(Title + " " + Percentage.ToString("0.0%") + " | " + RemainingTime.TotalHours.ToString("00") + ":" + RemainingTime.Minutes.ToString("00") + ":" + RemainingTime.Seconds.ToString("00"), SystemFonts.CaptionFont, Brushes.Black, picUpdate.DisplayRectangle, sF);
        //            }
        //            else
        //                e.Graphics.DrawString(Title + " " + Percentage.ToString("0.0%"), SystemFonts.CaptionFont, Brushes.Black, picUpdate.Bounds, sF);
        //        }
        //    }
        //}

        /// <summary>
        /// When the timer ticks, refresh the picture
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tmrUpdate_Tick(object sender, EventArgs e)
        {
            try
            {
                picUpdate.Refresh();
            }
            catch (Exception ex)
            {
                File.WriteAllText("MMLoader-TickUpdateError-" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".log", ex.ToString());
            }
        }

      
    }
}